﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace GeneralMatrix
{
    public class GeneralMatrix
    {
        protected int nRows;
        int nCols;
        protected double[,] elems;
        public GeneralMatrix()
        {
            nRows = 0;
            nCols = 0;
            elems = null;
        }
        public GeneralMatrix(int m, int n, double[,] elems)
        {
            ReadMatr(m, n, elems);
        }
        public void ReadMatr(int m, int n, double[,] elems)
        {
            this.elems = new double[m,n];
            for (int i = 0; i < m; i++)
                for (int j = 0; j < n; j++)
                    this.elems[i,j] = elems[i,j];
            nRows = m;
            nCols = n;
        }
        public void WriteMatr()
        {
            for (int i = 0; i < nRows; i++)
            {
                for (int j = 0; j < nCols; j++)
                    Console.Write(this.elems[i,j] + "\t");
                Console.WriteLine();
            }
        }
        public void Transpose()
        {
            double[,] newelems = new double[nCols, nRows];
            for (int i = 0; i < nRows; i++)            
                for (int j = 0; j < nCols; j++)
                    newelems[j,i] = elems[i,j];
            elems = newelems;
            int n = nRows;
            nRows = nCols;
            nCols = n;
        }
        public static GeneralMatrix operator +(GeneralMatrix M1, GeneralMatrix M2)
        {
            if(M1.nCols != M2.nCols || M1.nRows != M2.nRows)
                return null;
            GeneralMatrix Mres = new GeneralMatrix(M1.nRows, M1.nCols, M1.elems);
            for (int i = 0; i < M1.nRows; i++)
                for (int j = 1; j < M1.nCols; j++)
                    Mres.elems[i, j] += M2.elems[i,j];                
            return Mres;
        }
        public static GeneralMatrix operator -(GeneralMatrix M1, GeneralMatrix M2)
        {
            if(M1.nCols != M2.nCols || M1.nRows != M2.nRows)
                return null;
            GeneralMatrix Mres = new GeneralMatrix(M1.nRows, M2.nCols, M1.elems);
            for (int i = 0; i < M1.nRows; i++)
                for (int j = 1; j < M1.nCols; j++)
                    Mres.elems[i, j] -= M2.elems[i,j];                
            return Mres;
        }
        public static GeneralMatrix operator *(GeneralMatrix M1, GeneralMatrix M2)
        {
            if (M2.nRows != M1.nCols)
                return null;
            double[,] nullarray = new double[M1.nRows, M2.nCols];
            for (int i = 0; i < M1.nRows; i++)
                for (int j = 0; j < M2.nCols; j++)
                    nullarray[i, j] = 0;
            GeneralMatrix Mres = new GeneralMatrix(M1.nRows, M2.nCols, nullarray);
            for (int i = 0; i < M1.nRows; i++)
                for (int j = 0; j < M2.nCols; j++)
                {
                    for (int k = 0; k < M2.nCols; k++)
                        Mres.elems[i, j] += M1.elems[i, k] * M2.elems[k, j];
                }
            return Mres;
        }
    }

    public class UpperTriangleMatrix : GeneralMatrix
    {
        public UpperTriangleMatrix()
        {
            nRows = 0;
            elems = null;
        }
        public UpperTriangleMatrix(int n, double[,] elems):base(n, n, elems)
        {
            ReadMatr(n, elems);
            for (int i = 0; i < n; i++)
                for (int j = 0; j < i; j++)
                    this.elems[i, j] = 0;
        }
        public void ReadMatr(int n, double[,] elems)
        {
            this.elems = new double[n, n];
            for (int i = 0; i < n; i++)
                for (int j = i; j < n; j++)
                    this.elems[i, j] = elems[i, j];
            nRows = n;
        }
        public void Transpose()
        {
            double elem;
            for (int i = 0; i < nRows; i++)
                for (int j = i + 1; j < nRows; j++)
                {
                    elem = elems[i, j];
                    elems[i, j] = elems[j, i];
                    elems[j, i] = elem;
                }
        }
        public static UpperTriangleMatrix operator +(UpperTriangleMatrix M1, UpperTriangleMatrix M2)
        {
            if (M1.nRows != M2.nRows)
                return null;
            UpperTriangleMatrix Mres = new UpperTriangleMatrix(M1.nRows, M1.elems);
            for (int i = 0; i < M1.nRows; i++)
                for (int j = 1; j < M1.nRows; j++)
                    Mres.elems[i, j] += M2.elems[i, j];
            return Mres;
        }
        public static UpperTriangleMatrix operator -(UpperTriangleMatrix M1, UpperTriangleMatrix M2)
        {
            if (M1.nRows != M2.nRows)
                return null;
            UpperTriangleMatrix Mres = new UpperTriangleMatrix(M1.nRows, M1.elems);
            for (int i = 0; i < M1.nRows; i++)
                for (int j = 1; j < M1.nRows; j++)
                    Mres.elems[i, j] -= M2.elems[i, j];
            return Mres;
        }
        public static UpperTriangleMatrix operator *(UpperTriangleMatrix M1, UpperTriangleMatrix M2)
        {
            if (M2.nRows != M1.nRows)
                return null;
            double[,] nullarray = new double[M1.nRows, M2.nRows];
            for (int i = 0; i < M1.nRows; i++)
                for (int j = 0; j < M2.nRows; j++)
                    nullarray[i, j] = 0;
            UpperTriangleMatrix Mres = new UpperTriangleMatrix(M1.nRows, nullarray);
            for (int i = 0; i < M1.nRows; i++)
                for (int j = i; j < M1.nRows; j++)
                {
                    for (int k = 0; k < M1.nRows; k++)
                        Mres.elems[i, j] += M1.elems[i, k] * M2.elems[k, j];
                }
            return Mres;
        }
    }
    //-----------------------------------------------
    public class DiagonalMatrix : UpperTriangleMatrix
    {
        public DiagonalMatrix()
        {
            nRows = 0;
            elems = null;
        }
        public DiagonalMatrix(int n, double[,] elems): base(n, elems)
        {
            ReadMatr(n, elems);
            for (int i = 0; i < n; i++)
                for (int j = 0; j < n; j++)
                    if (i != j) this.elems[i, j] = 0;
        }
        public DiagonalMatrix(int n, double[] elems)
        {
            ReadMatr(n, elems);
            for (int i = 0; i < n; i++)
                for (int j = 0; j < n; j++)
                    if(i != j) this.elems[i, j] = 0;
        }
       public void ReadMatr(int n, double[] elems)
        {
            this.elems = new double[n, n];
            for (int i = 0; i < n; i++)
                    this.elems[i, i] = elems[i];
            nRows = n;
        }
        public static DiagonalMatrix operator +(DiagonalMatrix M1, DiagonalMatrix M2)
        {
            if (M1.nRows != M2.nRows)
                return null;
            DiagonalMatrix Mres = new DiagonalMatrix(M1.nRows, M1.elems);
            for (int i = 0; i < M1.nRows; i++)
                    Mres.elems[i, i] += M2.elems[i, i];
            return Mres;
        }
        public static DiagonalMatrix operator -(DiagonalMatrix M1, DiagonalMatrix M2)
        {
            if (M1.nRows != M2.nRows)
                return null;
            DiagonalMatrix Mres = new DiagonalMatrix(M1.nRows, M1.elems);
            for (int i = 0; i < M1.nRows; i++)
                Mres.elems[i, i] -= M2.elems[i, i];
            return Mres;
        }
        public static DiagonalMatrix operator *(DiagonalMatrix M1, DiagonalMatrix M2)
        {
            if (M1.nRows != M2.nRows)
                return null;
            DiagonalMatrix Mres = new DiagonalMatrix(M1.nRows, M1.elems);
            for (int i = 0; i < M1.nRows; i++)
                Mres.elems[i, i] *= M2.elems[i, i];
            return Mres;
        }
    }
    public class ScalarMatrix : DiagonalMatrix
    {
        public ScalarMatrix()
        {
            nRows = 0;
            elems = null;
        }
        public ScalarMatrix(int n, double[,] elems): base(n, elems)
        {
            ReadMatr(n, elems);
            for (int i = 0; i < n; i++)
                for (int j = 0; j < n; j++)
                    if (i != j) this.elems[i, j] = 0;
        }
        public ScalarMatrix(int n, double elem)
        {
            ReadMatr(n, elems);
            for (int i = 0; i < n; i++)
                for (int j = 0; j < n; j++)
                    if(i != j) this.elems[i, j] = 0;
        }
       public void ReadMatr(int n, double elem)
        {
            this.elems = new double[n, n];
            for (int i = 0; i < n; i++)
                    this.elems[i, i] = elem;
            nRows = n;
        }
        public static ScalarMatrix operator +(ScalarMatrix M1, ScalarMatrix M2)
        {
            if (M1.nRows != M2.nRows)
                return null;
            ScalarMatrix Mres = new ScalarMatrix(M1.nRows, M1.elems);
            double M2elem = M2.elems[0, 0];
            for (int i = 0; i < M1.nRows; i++)
                    Mres.elems[i, i] += M2elem;
            return Mres;
        }
        public static ScalarMatrix operator -(ScalarMatrix M1, ScalarMatrix M2)
        {
            if (M1.nRows != M2.nRows)
                return null;
            ScalarMatrix Mres = new ScalarMatrix(M1.nRows, M1.elems);
            double M2elem = M2.elems[0, 0];
            for (int i = 0; i < M1.nRows; i++)
                Mres.elems[i, i] -= M2elem;
            return Mres;
        }
        public static ScalarMatrix operator *(ScalarMatrix M1, ScalarMatrix M2)
        {
            if (M1.nRows != M2.nRows)
                return null;
            ScalarMatrix Mres = new ScalarMatrix(M1.nRows, M1.elems);
            double M2elem = M2.elems[0, 0];
            for (int i = 0; i < M1.nRows; i++)
                Mres.elems[i, i] *= M2elem;
            return Mres;
        }
    }

}
